<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#async-函数"  class="anchor" id="user-content-async-函数">

    </a>async 函数
    </h1>
    <h2><a href="#含义"  class="anchor" id="user-content-含义">

    </a>含义
    </h2>
    <p>ES2017 标准引入了 async 函数，使得异步操作变得更加方便。</p>
    <p>async 函数是什么？一句话，它就是 Generator 函数的语法糖。</p>
    <p>前文有一个 Generator 函数，依次读取两个文件。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">fs</span> <span
            class="pl-k">=</span> <span class="pl-c1">require</span>(<span class="pl-s"><span
            class="pl-pds">'</span>fs<span class="pl-pds">'</span></span>);

<span class="pl-k">const</span> <span class="pl-c1">readFile</span> <span class="pl-k">=</span> <span class="pl-k">function</span> (<span
                class="pl-smi">fileName</span>) {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-smi">fs</span>.<span class="pl-en">readFile</span>(fileName, <span
                class="pl-k">function</span>(<span class="pl-smi">error</span>, <span class="pl-smi">data</span>) {
      <span class="pl-k">if</span> (error) <span class="pl-k">return</span> <span class="pl-en">reject</span>(error);
      <span class="pl-en">resolve</span>(data);
    });
  });
};

<span class="pl-k">const</span> <span class="pl-c1">gen</span> <span class="pl-k">=</span> <span
                class="pl-k">function</span><span class="pl-k">*</span> () {
  <span class="pl-k">const</span> <span class="pl-c1">f1</span> <span class="pl-k">=</span> <span
                class="pl-k">yield</span> <span class="pl-en">readFile</span>(<span class="pl-s"><span
                class="pl-pds">'</span>/etc/fstab<span class="pl-pds">'</span></span>);
  <span class="pl-k">const</span> <span class="pl-c1">f2</span> <span class="pl-k">=</span> <span
                class="pl-k">yield</span> <span class="pl-en">readFile</span>(<span class="pl-s"><span
                class="pl-pds">'</span>/etc/shells<span class="pl-pds">'</span></span>);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">f1</span>.<span class="pl-c1">toString</span>());
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">f2</span>.<span class="pl-c1">toString</span>());
};</pre>
    </div>
    <p>写成<code>async</code>函数，就是下面这样。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">asyncReadFile</span> <span class="pl-k">=</span> <span class="pl-k">async</span> <span
            class="pl-k">function</span> () {
  <span class="pl-k">const</span> <span class="pl-c1">f1</span> <span class="pl-k">=</span> <span
                class="pl-k">await</span> <span class="pl-en">readFile</span>(<span class="pl-s"><span
                class="pl-pds">'</span>/etc/fstab<span class="pl-pds">'</span></span>);
  <span class="pl-k">const</span> <span class="pl-c1">f2</span> <span class="pl-k">=</span> <span
                class="pl-k">await</span> <span class="pl-en">readFile</span>(<span class="pl-s"><span
                class="pl-pds">'</span>/etc/shells<span class="pl-pds">'</span></span>);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">f1</span>.<span class="pl-c1">toString</span>());
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">f2</span>.<span class="pl-c1">toString</span>());
};</pre>
    </div>
    <p>一比较就会发现，<code>async</code>函数就是将 Generator 函数的星号（<code>*</code>）替换成<code>async</code>，将<code>yield</code>替换成<code>await</code>，仅此而已。
    </p>
    <p><code>async</code>函数对 Generator 函数的改进，体现在以下四点。</p>
    <p>（1）内置执行器。</p>
    <p>Generator 函数的执行必须靠执行器，所以才有了<code>co</code>模块，而<code>async</code>函数自带执行器。也就是说，<code>async</code>函数的执行，与普通函数一模一样，只要一行。
    </p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-en">asyncReadFile</span>();</pre>
    </div>
    <p>上面的代码调用了<code>asyncReadFile</code>函数，然后它就会自动执行，输出最后结果。这完全不像 Generator
        函数，需要调用<code>next</code>方法，或者用<code>co</code>模块，才能真正执行，得到最后结果。</p>
    <p>（2）更好的语义。</p>
    <p>
        <code>async</code>和<code>await</code>，比起星号和<code>yield</code>，语义更清楚了。<code>async</code>表示函数里有异步操作，<code>await</code>表示紧跟在后面的表达式需要等待结果。
    </p>
    <p>（3）更广的适用性。</p>
    <p><code>co</code>模块约定，<code>yield</code>命令后面只能是 Thunk 函数或 Promise 对象，而<code>async</code>函数的<code>await</code>命令后面，可以是
        Promise 对象和原始类型的值（数值、字符串和布尔值，但这时等同于同步操作）。</p>
    <p>（4）返回值是 Promise。</p>
    <p><code>async</code>函数的返回值是 Promise 对象，这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用<code>then</code>方法指定下一步的操作。</p>
    <p>进一步说，<code>async</code>函数完全可以看作多个异步操作，包装成的一个 Promise 对象，而<code>await</code>命令就是内部<code>then</code>命令的语法糖。</p>
    <h2><a href="#基本用法"  class="anchor" id="user-content-基本用法">

    </a>基本用法
    </h2>
    <p><code>async</code>函数返回一个 Promise 对象，可以使用<code>then</code>方法添加回调函数。当函数执行的时候，一旦遇到<code>await</code>就会先返回，等到异步操作完成，再接着执行函数体内后面的语句。
    </p>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">getStockPriceByName</span>(<span class="pl-smi">name</span>) {
  <span class="pl-k">const</span> <span class="pl-c1">symbol</span> <span class="pl-k">=</span> <span
                class="pl-k">await</span> <span class="pl-en">getStockSymbol</span>(name);
  <span class="pl-k">const</span> <span class="pl-c1">stockPrice</span> <span class="pl-k">=</span> <span class="pl-k">await</span> <span
                class="pl-en">getStockPrice</span>(symbol);
  <span class="pl-k">return</span> stockPrice;
}

<span class="pl-en">getStockPriceByName</span>(<span class="pl-s"><span class="pl-pds">'</span>goog<span class="pl-pds">'</span></span>).<span
                class="pl-en">then</span>(<span class="pl-k">function</span> (<span class="pl-smi">result</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(result);
});</pre>
    </div>
    <p>上面代码是一个获取股票报价的函数，函数前面的<code>async</code>关键字，表明该函数内部有异步操作。调用该函数时，会立即返回一个<code>Promise</code>对象。</p>
    <p>下面是另一个例子，指定多少毫秒后输出一个值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">timeout</span>(<span class="pl-smi">ms</span>) {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>((<span
                class="pl-smi">resolve</span>) <span class="pl-k">=&gt;</span> {
    <span class="pl-c1">setTimeout</span>(resolve, ms);
  });
}

<span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">asyncPrint</span>(<span
                class="pl-smi">value</span>, <span class="pl-smi">ms</span>) {
  <span class="pl-k">await</span> <span class="pl-en">timeout</span>(ms);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(value);
}

<span class="pl-en">asyncPrint</span>(<span class="pl-s"><span class="pl-pds">'</span>hello world<span
                class="pl-pds">'</span></span>, <span class="pl-c1">50</span>);</pre>
    </div>
    <p>上面代码指定 50 毫秒以后，输出<code>hello world</code>。</p>
    <p>由于<code>async</code>函数返回的是 Promise 对象，可以作为<code>await</code>命令的参数。所以，上面的例子也可以写成下面的形式。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">timeout</span>(<span class="pl-smi">ms</span>) {
  <span class="pl-k">await</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>((<span
                class="pl-smi">resolve</span>) <span class="pl-k">=&gt;</span> {
    <span class="pl-c1">setTimeout</span>(resolve, ms);
  });
}

<span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">asyncPrint</span>(<span
                class="pl-smi">value</span>, <span class="pl-smi">ms</span>) {
  <span class="pl-k">await</span> <span class="pl-en">timeout</span>(ms);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(value);
}

<span class="pl-en">asyncPrint</span>(<span class="pl-s"><span class="pl-pds">'</span>hello world<span
                class="pl-pds">'</span></span>, <span class="pl-c1">50</span>);</pre>
    </div>
    <p>async 函数有多种使用形式。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 函数声明</span>
<span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">foo</span>() {}

<span class="pl-c"><span class="pl-c">//</span> 函数表达式</span>
<span class="pl-k">const</span> <span class="pl-c1">foo</span> <span class="pl-k">=</span> <span
                class="pl-k">async</span> <span class="pl-k">function</span> () {};

<span class="pl-c"><span class="pl-c">//</span> 对象的方法</span>
<span class="pl-k">let</span> obj <span class="pl-k">=</span> { <span class="pl-k">async</span> <span
                class="pl-en">foo</span>() {} };
<span class="pl-smi">obj</span>.<span class="pl-en">foo</span>().<span class="pl-en">then</span>(<span
                class="pl-k">...</span>)

<span class="pl-c"><span class="pl-c">//</span> Class 的方法</span>
<span class="pl-k">class</span> <span class="pl-en">Storage</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">this</span>.<span class="pl-smi">cachePromise</span> <span class="pl-k">=</span> <span
                class="pl-smi">caches</span>.<span class="pl-c1">open</span>(<span class="pl-s"><span
                class="pl-pds">'</span>avatars<span class="pl-pds">'</span></span>);
  }

  <span class="pl-k">async</span> <span class="pl-en">getAvatar</span>(<span class="pl-smi">name</span>) {
    <span class="pl-k">const</span> <span class="pl-c1">cache</span> <span class="pl-k">=</span> <span class="pl-k">await</span> <span
                class="pl-c1">this</span>.<span class="pl-smi">cachePromise</span>;
    <span class="pl-k">return</span> <span class="pl-smi">cache</span>.<span class="pl-c1">match</span>(<span
                class="pl-s"><span class="pl-pds">`</span>/avatars/<span class="pl-s1"><span class="pl-pse">${</span>name<span
                class="pl-pse">}</span></span>.jpg<span class="pl-pds">`</span></span>);
  }
}

<span class="pl-k">const</span> <span class="pl-c1">storage</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Storage</span>();
<span class="pl-smi">storage</span>.<span class="pl-en">getAvatar</span>(<span class="pl-s"><span
                class="pl-pds">'</span>jake<span class="pl-pds">'</span></span>).<span class="pl-en">then</span>(…);

<span class="pl-c"><span class="pl-c">//</span> 箭头函数</span>
<span class="pl-k">const</span> <span class="pl-c1">foo</span> <span class="pl-k">=</span> <span
                class="pl-k">async</span> () <span class="pl-k">=&gt;</span> {};</pre>
    </div>
    <h2><a href="#语法"  class="anchor" id="user-content-语法">

    </a>语法
    </h2>
    <p><code>async</code>函数的语法规则总体上比较简单，难点是错误处理机制。</p>
    <h3><a href="#返回-promise-对象"  class="anchor" id="user-content-返回-promise-对象">

    </a>返回 Promise 对象
    </h3>
    <p><code>async</code>函数返回一个 Promise 对象。</p>
    <p><code>async</code>函数内部<code>return</code>语句返回的值，会成为<code>then</code>方法回调函数的参数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>hello world<span
                class="pl-pds">'</span></span>;
}

<span class="pl-en">f</span>().<span class="pl-en">then</span>(<span class="pl-smi">v</span> <span
                class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span class="pl-c1">log</span>(v))
<span class="pl-c"><span class="pl-c">//</span> "hello world"</span></pre>
    </div>
    <p>上面代码中，函数<code>f</code>内部<code>return</code>命令返回的值，会被<code>then</code>方法回调函数接收到。</p>
    <p><code>async</code>函数内部抛出错误，会导致返回的 Promise 对象变为<code>reject</code>状态。抛出的错误对象会被<code>catch</code>方法回调函数接收到。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>出错了<span class="pl-pds">'</span></span>);
}

<span class="pl-en">f</span>().<span class="pl-en">then</span>(
  <span class="pl-smi">v</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span class="pl-c1">log</span>(v),
  <span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span class="pl-c1">log</span>(e)
)
<span class="pl-c"><span class="pl-c">//</span> Error: 出错了</span></pre>
    </div>
    <h3><a href="#promise-对象的状态变化"  class="anchor" id="user-content-promise-对象的状态变化">

    </a>Promise 对象的状态变化
    </h3>
    <p><code>async</code>函数返回的 Promise 对象，必须等到内部所有<code>await</code>命令后面的 Promise 对象执行完，才会发生状态改变，除非遇到<code>return</code>语句或者抛出错误。也就是说，只有<code>async</code>函数内部的异步操作执行完，才会执行<code>then</code>方法指定的回调函数。
    </p>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">getTitle</span>(<span class="pl-smi">url</span>) {
  <span class="pl-k">let</span> response <span class="pl-k">=</span> <span class="pl-k">await</span> <span
                class="pl-en">fetch</span>(url);
  <span class="pl-k">let</span> html <span class="pl-k">=</span> <span class="pl-k">await</span> <span class="pl-smi">response</span>.<span
                class="pl-c1">text</span>();
  <span class="pl-k">return</span> <span class="pl-smi">html</span>.<span class="pl-c1">match</span>(<span
                class="pl-sr"><span class="pl-pds">/</span>&lt;title&gt;(<span class="pl-c1">[<span
                class="pl-c1">\s\S</span>]</span><span class="pl-k">+</span>)&lt;<span class="pl-cce">\/</span>title&gt;<span
                class="pl-pds">/</span>i</span>)[<span class="pl-c1">1</span>];
}
<span class="pl-en">getTitle</span>(<span class="pl-s"><span class="pl-pds">'</span>https://tc39.github.io/ecma262/<span
                class="pl-pds">'</span></span>).<span class="pl-en">then</span>(<span class="pl-en">console</span>.<span
                class="pl-smi">log</span>)
<span class="pl-c"><span class="pl-c">//</span> "ECMAScript 2017 Language Specification"</span></pre>
    </div>
    <p>
        上面代码中，函数<code>getTitle</code>内部有三个操作：抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成，才会执行<code>then</code>方法里面的<code>console.log</code>。
    </p>
    <h3><a href="#await-命令"  class="anchor" id="user-content-await-命令">

    </a>await 命令
    </h3>
    <p>正常情况下，<code>await</code>命令后面是一个 Promise 对象。如果不是，会被转成一个立即<code>resolve</code>的 Promise 对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">return</span> <span class="pl-k">await</span> <span class="pl-c1">123</span>;
}

<span class="pl-en">f</span>().<span class="pl-en">then</span>(<span class="pl-smi">v</span> <span
                class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span class="pl-c1">log</span>(v))
<span class="pl-c"><span class="pl-c">//</span> 123</span></pre>
    </div>
    <p>上面代码中，<code>await</code>命令的参数是数值<code>123</code>，它被转成 Promise 对象，并立即<code>resolve</code>。</p>
    <p><code>await</code>命令后面的 Promise 对象如果变为<code>reject</code>状态，则<code>reject</code>的参数会被<code>catch</code>方法的回调函数接收到。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">await</span> <span class="pl-c1">Promise</span>.<span class="pl-en">reject</span>(<span
                class="pl-s"><span class="pl-pds">'</span>出错了<span class="pl-pds">'</span></span>);
}

<span class="pl-en">f</span>()
.<span class="pl-en">then</span>(<span class="pl-smi">v</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(v))
.<span class="pl-en">catch</span>(<span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(e))
<span class="pl-c"><span class="pl-c">//</span> 出错了</span></pre>
    </div>
    <p>注意，上面代码中，<code>await</code>语句前面没有<code>return</code>，但是<code>reject</code>方法的参数依然传入了<code>catch</code>方法的回调函数。这里如果在<code>await</code>前面加上<code>return</code>，效果是一样的。
    </p>
    <p>只要一个<code>await</code>语句后面的 Promise 变为<code>reject</code>，那么整个<code>async</code>函数都会中断执行。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">await</span> <span class="pl-c1">Promise</span>.<span class="pl-en">reject</span>(<span
                class="pl-s"><span class="pl-pds">'</span>出错了<span class="pl-pds">'</span></span>);
  <span class="pl-k">await</span> <span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>(<span
                class="pl-s"><span class="pl-pds">'</span>hello world<span class="pl-pds">'</span></span>); <span
                class="pl-c"><span class="pl-c">//</span> 不会执行</span>
}</pre>
    </div>
    <p>上面代码中，第二个<code>await</code>语句是不会执行的，因为第一个<code>await</code>语句状态变成了<code>reject</code>。</p>
    <p>有时，我们希望即使前一个异步操作失败，也不要中断后面的异步操作。这时可以将第一个<code>await</code>放在<code>try...catch</code>结构里面，这样不管这个异步操作是否成功，第二个<code>await</code>都会执行。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">try</span> {
    <span class="pl-k">await</span> <span class="pl-c1">Promise</span>.<span class="pl-en">reject</span>(<span
                class="pl-s"><span class="pl-pds">'</span>出错了<span class="pl-pds">'</span></span>);
  } <span class="pl-k">catch</span>(e) {
  }
  <span class="pl-k">return</span> <span class="pl-k">await</span> <span class="pl-c1">Promise</span>.<span
                class="pl-en">resolve</span>(<span class="pl-s"><span class="pl-pds">'</span>hello world<span
                class="pl-pds">'</span></span>);
}

<span class="pl-en">f</span>()
.<span class="pl-en">then</span>(<span class="pl-smi">v</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(v))
<span class="pl-c"><span class="pl-c">//</span> hello world</span></pre>
    </div>
    <p>另一种方法是<code>await</code>后面的 Promise 对象再跟一个<code>catch</code>方法，处理前面可能出现的错误。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">await</span> <span class="pl-c1">Promise</span>.<span class="pl-en">reject</span>(<span
                class="pl-s"><span class="pl-pds">'</span>出错了<span class="pl-pds">'</span></span>)
    .<span class="pl-en">catch</span>(<span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(e));
  <span class="pl-k">return</span> <span class="pl-k">await</span> <span class="pl-c1">Promise</span>.<span
                class="pl-en">resolve</span>(<span class="pl-s"><span class="pl-pds">'</span>hello world<span
                class="pl-pds">'</span></span>);
}

<span class="pl-en">f</span>()
.<span class="pl-en">then</span>(<span class="pl-smi">v</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(v))
<span class="pl-c"><span class="pl-c">//</span> 出错了</span>
<span class="pl-c"><span class="pl-c">//</span> hello world</span></pre>
    </div>
    <h3><a href="#错误处理"  class="anchor" id="user-content-错误处理">

    </a>错误处理
    </h3>
    <p>如果<code>await</code>后面的异步操作出错，那么等同于<code>async</code>函数返回的 Promise 对象被<code>reject</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">await</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>出错了<span class="pl-pds">'</span></span>);
  });
}

<span class="pl-en">f</span>()
.<span class="pl-en">then</span>(<span class="pl-smi">v</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(v))
.<span class="pl-en">catch</span>(<span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(e))
<span class="pl-c"><span class="pl-c">//</span> Error：出错了</span></pre>
    </div>
    <p>上面代码中，<code>async</code>函数<code>f</code>执行后，<code>await</code>后面的 Promise 对象会抛出一个错误对象，导致<code>catch</code>方法的回调函数被调用，它的参数就是抛出的错误对象。具体的执行机制，可以参考后文的“async
        函数的实现原理”。</p>
    <p>防止出错的方法，也是将其放在<code>try...catch</code>代码块之中。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">try</span> {
    <span class="pl-k">await</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
      <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span class="pl-s"><span
                class="pl-pds">'</span>出错了<span class="pl-pds">'</span></span>);
    });
  } <span class="pl-k">catch</span>(e) {
  }
  <span class="pl-k">return</span> <span class="pl-k">await</span>(<span class="pl-s"><span class="pl-pds">'</span>hello world<span
                class="pl-pds">'</span></span>);
}</pre>
    </div>
    <p>如果有多个<code>await</code>命令，可以统一放在<code>try...catch</code>结构中。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">main</span>() {
  <span class="pl-k">try</span> {
    <span class="pl-k">const</span> <span class="pl-c1">val1</span> <span class="pl-k">=</span> <span
                class="pl-k">await</span> <span class="pl-en">firstStep</span>();
    <span class="pl-k">const</span> <span class="pl-c1">val2</span> <span class="pl-k">=</span> <span
                class="pl-k">await</span> <span class="pl-en">secondStep</span>(val1);
    <span class="pl-k">const</span> <span class="pl-c1">val3</span> <span class="pl-k">=</span> <span
                class="pl-k">await</span> <span class="pl-en">thirdStep</span>(val1, val2);

    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>Final: <span
                class="pl-pds">'</span></span>, val3);
  }
  <span class="pl-k">catch</span> (err) {
    <span class="pl-en">console</span>.<span class="pl-c1">error</span>(err);
  }
}</pre>
    </div>
    <p>下面的例子使用<code>try...catch</code>结构，实现多次重复尝试。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">superagent</span> <span class="pl-k">=</span> <span class="pl-c1">require</span>(<span
            class="pl-s"><span class="pl-pds">'</span>superagent<span class="pl-pds">'</span></span>);
<span class="pl-k">const</span> <span class="pl-c1">NUM_RETRIES</span> <span class="pl-k">=</span> <span
                class="pl-c1">3</span>;

<span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">test</span>() {
  <span class="pl-k">let</span> i;
  <span class="pl-k">for</span> (i <span class="pl-k">=</span> <span class="pl-c1">0</span>; i <span
                class="pl-k">&lt;</span> <span class="pl-c1">NUM_RETRIES</span>; <span class="pl-k">++</span>i) {
    <span class="pl-k">try</span> {
      <span class="pl-k">await</span> <span class="pl-smi">superagent</span>.<span class="pl-c1">get</span>(<span
                class="pl-s"><span class="pl-pds">'</span>http://google.com/this-throws-an-error<span
                class="pl-pds">'</span></span>);
      <span class="pl-k">break</span>;
    } <span class="pl-k">catch</span>(err) {}
  }
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(i); <span class="pl-c"><span class="pl-c">//</span> 3</span>
}

<span class="pl-en">test</span>();</pre>
    </div>
    <p>上面代码中，如果<code>await</code>操作成功，就会使用<code>break</code>语句退出循环；如果失败，会被<code>catch</code>语句捕捉，然后进入下一轮循环。</p>
    <h3><a href="#使用注意点"  class="anchor" id="user-content-使用注意点">

    </a>使用注意点
    </h3>
    <p>第一点，前面已经说过，<code>await</code>命令后面的<code>Promise</code>对象，运行结果可能是<code>rejected</code>，所以最好把<code>await</code>命令放在<code>try...catch</code>代码块中。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">myFunction</span>() {
  <span class="pl-k">try</span> {
    <span class="pl-k">await</span> <span class="pl-en">somethingThatReturnsAPromise</span>();
  } <span class="pl-k">catch</span> (err) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(err);
  }
}

<span class="pl-c"><span class="pl-c">//</span> 另一种写法</span>

<span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">myFunction</span>() {
  <span class="pl-k">await</span> <span class="pl-en">somethingThatReturnsAPromise</span>()
  .<span class="pl-en">catch</span>(<span class="pl-k">function</span> (<span class="pl-smi">err</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(err);
  });
}</pre>
    </div>
    <p>第二点，多个<code>await</code>命令后面的异步操作，如果不存在继发关系，最好让它们同时触发。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> foo <span class="pl-k">=</span> <span
            class="pl-k">await</span> <span class="pl-en">getFoo</span>();
<span class="pl-k">let</span> bar <span class="pl-k">=</span> <span class="pl-k">await</span> <span
                class="pl-en">getBar</span>();</pre>
    </div>
    <p>
        上面代码中，<code>getFoo</code>和<code>getBar</code>是两个独立的异步操作（即互不依赖），被写成继发关系。这样比较耗时，因为只有<code>getFoo</code>完成以后，才会执行<code>getBar</code>，完全可以让它们同时触发。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 写法一</span>
<span class="pl-k">let</span> [foo, bar] <span class="pl-k">=</span> <span class="pl-k">await</span> <span
                class="pl-c1">Promise</span>.<span class="pl-c1">all</span>([<span class="pl-en">getFoo</span>(), <span
                class="pl-en">getBar</span>()]);

<span class="pl-c"><span class="pl-c">//</span> 写法二</span>
<span class="pl-k">let</span> fooPromise <span class="pl-k">=</span> <span class="pl-en">getFoo</span>();
<span class="pl-k">let</span> barPromise <span class="pl-k">=</span> <span class="pl-en">getBar</span>();
<span class="pl-k">let</span> foo <span class="pl-k">=</span> <span class="pl-k">await</span> fooPromise;
<span class="pl-k">let</span> bar <span class="pl-k">=</span> <span class="pl-k">await</span> barPromise;</pre>
    </div>
    <p>上面两种写法，<code>getFoo</code>和<code>getBar</code>都是同时触发，这样就会缩短程序的执行时间。</p>
    <p>第三点，<code>await</code>命令只能用在<code>async</code>函数之中，如果用在普通函数，就会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">dbFuc</span>(<span class="pl-smi">db</span>) {
  <span class="pl-k">let</span> docs <span class="pl-k">=</span> [{}, {}, {}];

  <span class="pl-c"><span class="pl-c">//</span> 报错</span>
  <span class="pl-smi">docs</span>.<span class="pl-c1">forEach</span>(<span class="pl-k">function</span> (<span
                class="pl-smi">doc</span>) {
    <span class="pl-k">await</span> <span class="pl-smi">db</span>.<span class="pl-en">post</span>(doc);
  });
}</pre>
    </div>
    <p>上面代码会报错，因为<code>await</code>用在普通函数之中了。但是，如果将<code>forEach</code>方法的参数改成<code>async</code>函数，也有问题。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">dbFuc</span>(<span
            class="pl-smi">db</span>) { <span class="pl-c"><span class="pl-c">//</span>这里不需要 async</span>
 &nbsp;<span class="pl-k">let</span> docs <span class="pl-k">=</span> [{}, {}, {}];

  <span class="pl-c"><span class="pl-c">//</span> 可能得到错误结果</span>
  <span class="pl-smi">docs</span>.<span class="pl-c1">forEach</span>(<span class="pl-k">async</span> <span
                class="pl-k">function</span> (<span class="pl-smi">doc</span>) {
    <span class="pl-k">await</span> <span class="pl-smi">db</span>.<span class="pl-en">post</span>(doc);
  });
}</pre>
    </div>
    <p>上面代码可能不会正常工作，原因是这时三个<code>db.post</code>操作将是并发执行，也就是同时执行，而不是继发执行。正确的写法是采用<code>for</code>循环。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">dbFuc</span>(<span class="pl-smi">db</span>) {
  <span class="pl-k">let</span> docs <span class="pl-k">=</span> [{}, {}, {}];

  <span class="pl-k">for</span> (<span class="pl-k">let</span> doc <span class="pl-k">of</span> docs) {
    <span class="pl-k">await</span> <span class="pl-smi">db</span>.<span class="pl-en">post</span>(doc);
  }
}</pre>
    </div>
    <p>如果确实希望多个请求并发执行，可以使用<code>Promise.all</code>方法。当三个请求都会<code>resolved</code>时，下面两种写法效果相同。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">dbFuc</span>(<span class="pl-smi">db</span>) {
  <span class="pl-k">let</span> docs <span class="pl-k">=</span> [{}, {}, {}];
  <span class="pl-k">let</span> promises <span class="pl-k">=</span> <span class="pl-smi">docs</span>.<span
                class="pl-en">map</span>((<span class="pl-smi">doc</span>) <span class="pl-k">=&gt;</span> <span
                class="pl-smi">db</span>.<span class="pl-en">post</span>(doc));

  <span class="pl-k">let</span> results <span class="pl-k">=</span> <span class="pl-k">await</span> <span class="pl-c1">Promise</span>.<span
                class="pl-c1">all</span>(promises);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(results);
}

<span class="pl-c"><span class="pl-c">//</span> 或者使用下面的写法</span>

<span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">dbFuc</span>(<span
                class="pl-smi">db</span>) {
  <span class="pl-k">let</span> docs <span class="pl-k">=</span> [{}, {}, {}];
  <span class="pl-k">let</span> promises <span class="pl-k">=</span> <span class="pl-smi">docs</span>.<span
                class="pl-en">map</span>((<span class="pl-smi">doc</span>) <span class="pl-k">=&gt;</span> <span
                class="pl-smi">db</span>.<span class="pl-en">post</span>(doc));

  <span class="pl-k">let</span> results <span class="pl-k">=</span> [];
  <span class="pl-k">for</span> (<span class="pl-k">let</span> promise <span class="pl-k">of</span> promises) {
    <span class="pl-smi">results</span>.<span class="pl-c1">push</span>(<span class="pl-k">await</span> promise);
  }
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(results);
}</pre>
    </div>
    <p>目前，<a href="https://www.npmjs.com/package/@std/esm"
             rel="nofollow"><code>@std/esm</code></a>模块加载器支持顶层<code>await</code>，即<code>await</code>命令可以不放在 async
        函数里面，直接使用。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> async 函数的写法</span>
<span class="pl-k">const</span> <span class="pl-c1">start</span> <span class="pl-k">=</span> <span
                class="pl-k">async</span> () <span class="pl-k">=&gt;</span> {
  <span class="pl-k">const</span> <span class="pl-c1">res</span> <span class="pl-k">=</span> <span
                class="pl-k">await</span> <span class="pl-en">fetch</span>(<span class="pl-s"><span
                class="pl-pds">'</span>google.com<span class="pl-pds">'</span></span>);
  <span class="pl-k">return</span> <span class="pl-smi">res</span>.<span class="pl-c1">text</span>();
};

<span class="pl-en">start</span>().<span class="pl-en">then</span>(<span class="pl-en">console</span>.<span
                class="pl-smi">log</span>);

<span class="pl-c"><span class="pl-c">//</span> 顶层 await 的写法</span>
<span class="pl-k">const</span> <span class="pl-c1">res</span> <span class="pl-k">=</span> <span
                class="pl-k">await</span> <span class="pl-en">fetch</span>(<span class="pl-s"><span
                class="pl-pds">'</span>google.com<span class="pl-pds">'</span></span>);
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-k">await</span> <span class="pl-smi">res</span>.<span
                class="pl-c1">text</span>());</pre>
    </div>
    <p>上面代码中，第二种写法的脚本必须使用<code>@std/esm</code>加载器，才会生效。</p>
    <h2><a href="#async-函数的实现原理"  class="anchor" id="user-content-async-函数的实现原理">

    </a>async 函数的实现原理
    </h2>
    <p>async 函数的实现原理，就是将 Generator 函数和自动执行器，包装在一个函数里。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">fn</span>(<span class="pl-smi">args</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>

<span class="pl-k">function</span> <span class="pl-en">fn</span>(<span class="pl-smi">args</span>) {
  <span class="pl-k">return</span> <span class="pl-en">spawn</span>(<span class="pl-k">function</span><span
                class="pl-k">*</span> () {
    <span class="pl-c"><span class="pl-c">//</span> ...</span>
  });
}</pre>
    </div>
    <p>所有的<code>async</code>函数都可以写成上面的第二种形式，其中的<code>spawn</code>函数就是自动执行器。</p>
    <p>下面给出<code>spawn</code>函数的实现，基本就是前文自动执行器的翻版。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">spawn</span>(<span
            class="pl-smi">genF</span>) {
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-en">Promise</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) {
    <span class="pl-k">const</span> <span class="pl-c1">gen</span> <span class="pl-k">=</span> <span
                class="pl-en">genF</span>();
    <span class="pl-k">function</span> <span class="pl-en">step</span>(<span class="pl-smi">nextF</span>) {
      <span class="pl-k">let</span> next;
      <span class="pl-k">try</span> {
        next <span class="pl-k">=</span> <span class="pl-en">nextF</span>();
      } <span class="pl-k">catch</span>(e) {
        <span class="pl-k">return</span> <span class="pl-en">reject</span>(e);
      }
      <span class="pl-k">if</span>(<span class="pl-smi">next</span>.<span class="pl-smi">done</span>) {
        <span class="pl-k">return</span> <span class="pl-en">resolve</span>(<span class="pl-smi">next</span>.<span
                class="pl-c1">value</span>);
      }
      <span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>(<span class="pl-smi">next</span>.<span
                class="pl-c1">value</span>).<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">v</span>) {
        <span class="pl-en">step</span>(<span class="pl-k">function</span>() { <span class="pl-k">return</span> <span
                class="pl-smi">gen</span>.<span class="pl-c1">next</span>(v); });
      }, <span class="pl-k">function</span>(<span class="pl-smi">e</span>) {
        <span class="pl-en">step</span>(<span class="pl-k">function</span>() { <span class="pl-k">return</span> <span
                class="pl-smi">gen</span>.<span class="pl-en">throw</span>(e); });
      });
    }
    <span class="pl-en">step</span>(<span class="pl-k">function</span>() { <span class="pl-k">return</span> <span
                class="pl-smi">gen</span>.<span class="pl-c1">next</span>(<span class="pl-c1">undefined</span>); });
  });
}</pre>
    </div>
    <h2><a href="#与其他异步处理方法的比较"  class="anchor" id="user-content-与其他异步处理方法的比较">

    </a>与其他异步处理方法的比较
    </h2>
    <p>我们通过一个例子，来看 async 函数与 Promise、Generator 函数的比较。</p>
    <p>假定某个 DOM 元素上面，部署了一系列的动画，前一个动画结束，才能开始后一个。如果当中有一个动画出错，就不再往下执行，返回上一个成功执行的动画的返回值。</p>
    <p>首先是 Promise 的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">chainAnimationsPromise</span>(<span
            class="pl-smi">elem</span>, <span class="pl-smi">animations</span>) {

  <span class="pl-c"><span class="pl-c">//</span> 变量ret用来保存上一个动画的返回值</span>
  <span class="pl-k">let</span> ret <span class="pl-k">=</span> <span class="pl-c1">null</span>;

  <span class="pl-c"><span class="pl-c">//</span> 新建一个空的Promise</span>
  <span class="pl-k">let</span> p <span class="pl-k">=</span> <span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>();

  <span class="pl-c"><span class="pl-c">//</span> 使用then方法，添加所有动画</span>
  <span class="pl-k">for</span>(<span class="pl-k">let</span> anim <span class="pl-k">of</span> animations) {
    p <span class="pl-k">=</span> <span class="pl-smi">p</span>.<span class="pl-en">then</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">val</span>) {
      ret <span class="pl-k">=</span> val;
      <span class="pl-k">return</span> <span class="pl-en">anim</span>(elem);
    });
  }

  <span class="pl-c"><span class="pl-c">//</span> 返回一个部署了错误捕捉机制的Promise</span>
  <span class="pl-k">return</span> <span class="pl-smi">p</span>.<span class="pl-en">catch</span>(<span class="pl-k">function</span>(<span
                class="pl-smi">e</span>) {
    <span class="pl-c"><span class="pl-c">/*</span> 忽略错误，继续执行 <span class="pl-c">*/</span></span>
  }).<span class="pl-en">then</span>(<span class="pl-k">function</span>() {
    <span class="pl-k">return</span> ret;
  });

}</pre>
    </div>
    <p>虽然 Promise 的写法比回调函数的写法大大改进，但是一眼看上去，代码完全都是 Promise 的 API（<code>then</code>、<code>catch</code>等等），操作本身的语义反而不容易看出来。
    </p>
    <p>接着是 Generator 函数的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">chainAnimationsGenerator</span>(<span
            class="pl-smi">elem</span>, <span class="pl-smi">animations</span>) {

  <span class="pl-k">return</span> <span class="pl-en">spawn</span>(<span class="pl-k">function</span><span
                class="pl-k">*</span>() {
    <span class="pl-k">let</span> ret <span class="pl-k">=</span> <span class="pl-c1">null</span>;
    <span class="pl-k">try</span> {
      <span class="pl-k">for</span>(<span class="pl-k">let</span> anim <span class="pl-k">of</span> animations) {
        ret <span class="pl-k">=</span> <span class="pl-k">yield</span> <span class="pl-en">anim</span>(elem);
      }
    } <span class="pl-k">catch</span>(e) {
      <span class="pl-c"><span class="pl-c">/*</span> 忽略错误，继续执行 <span class="pl-c">*/</span></span>
    }
    <span class="pl-k">return</span> ret;
  });

}</pre>
    </div>
    <p>上面代码使用 Generator 函数遍历了每个动画，语义比 Promise 写法更清晰，用户定义的操作全部都出现在<code>spawn</code>函数的内部。这个写法的问题在于，必须有一个任务运行器，自动执行
        Generator 函数，上面代码的<code>spawn</code>函数就是自动执行器，它返回一个 Promise 对象，而且必须保证<code>yield</code>语句后面的表达式，必须返回一个 Promise。
    </p>
    <p>最后是 async 函数的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">chainAnimationsAsync</span>(<span
            class="pl-smi">elem</span>, <span class="pl-smi">animations</span>) {
  <span class="pl-k">let</span> ret <span class="pl-k">=</span> <span class="pl-c1">null</span>;
  <span class="pl-k">try</span> {
    <span class="pl-k">for</span>(<span class="pl-k">let</span> anim <span class="pl-k">of</span> animations) {
      ret <span class="pl-k">=</span> <span class="pl-k">await</span> <span class="pl-en">anim</span>(elem);
    }
  } <span class="pl-k">catch</span>(e) {
    <span class="pl-c"><span class="pl-c">/*</span> 忽略错误，继续执行 <span class="pl-c">*/</span></span>
  }
  <span class="pl-k">return</span> ret;
}</pre>
    </div>
    <p>可以看到 Async 函数的实现最简洁，最符合语义，几乎没有语义不相关的代码。它将 Generator 写法中的自动执行器，改在语言层面提供，不暴露给用户，因此代码量最少。如果使用 Generator
        写法，自动执行器需要用户自己提供。</p>
    <h2><a href="#实例按顺序完成异步操作"  class="anchor" id="user-content-实例按顺序完成异步操作">

    </a>实例：按顺序完成异步操作
    </h2>
    <p>实际开发中，经常遇到一组异步操作，需要按照顺序完成。比如，依次远程读取一组 URL，然后按照读取的顺序输出结果。</p>
    <p>Promise 的写法如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">logInOrder</span>(<span class="pl-smi">urls</span>) {
  <span class="pl-c"><span class="pl-c">//</span> 远程读取所有URL</span>
  <span class="pl-k">const</span> <span class="pl-c1">textPromises</span> <span class="pl-k">=</span> <span
                class="pl-smi">urls</span>.<span class="pl-en">map</span>(<span class="pl-smi">url</span> <span
                class="pl-k">=&gt;</span> {
    <span class="pl-k">return</span> <span class="pl-en">fetch</span>(url).<span class="pl-en">then</span>(<span
                class="pl-smi">response</span> <span class="pl-k">=&gt;</span> <span
                class="pl-smi">response</span>.<span class="pl-c1">text</span>());
  });

  <span class="pl-c"><span class="pl-c">//</span> 按次序输出</span>
  <span class="pl-smi">textPromises</span>.<span class="pl-en">reduce</span>((<span class="pl-smi">chain</span>, <span
                class="pl-smi">textPromise</span>) <span class="pl-k">=&gt;</span> {
    <span class="pl-k">return</span> <span class="pl-smi">chain</span>.<span class="pl-en">then</span>(() <span
                class="pl-k">=&gt;</span> textPromise)
      .<span class="pl-en">then</span>(<span class="pl-smi">text</span> <span class="pl-k">=&gt;</span> <span
                class="pl-en">console</span>.<span class="pl-c1">log</span>(text));
  }, <span class="pl-c1">Promise</span>.<span class="pl-en">resolve</span>());
}</pre>
    </div>
    <p>上面代码使用<code>fetch</code>方法，同时远程读取一组 URL。每个<code>fetch</code>操作都返回一个 Promise
        对象，放入<code>textPromises</code>数组。然后，<code>reduce</code>方法依次处理每个 Promise 对象，然后使用<code>then</code>，将所有 Promise
        对象连起来，因此就可以依次输出结果。</p>
    <p>这种写法不太直观，可读性比较差。下面是 async 函数实现。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">logInOrder</span>(<span class="pl-smi">urls</span>) {
  <span class="pl-k">for</span> (<span class="pl-k">const</span> <span class="pl-c1">url</span> <span
                class="pl-k">of</span> urls) {
    <span class="pl-k">const</span> <span class="pl-c1">response</span> <span class="pl-k">=</span> <span class="pl-k">await</span> <span
                class="pl-en">fetch</span>(url);
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-k">await</span> <span
                class="pl-smi">response</span>.<span class="pl-c1">text</span>());
  }
}</pre>
    </div>
    <p>上面代码确实大大简化，问题是所有远程操作都是继发。只有前一个 URL 返回结果，才会去读取下一个 URL，这样做效率很差，非常浪费时间。我们需要的是并发发出远程请求。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">logInOrder</span>(<span class="pl-smi">urls</span>) {
  <span class="pl-c"><span class="pl-c">//</span> 并发读取远程URL</span>
  <span class="pl-k">const</span> <span class="pl-c1">textPromises</span> <span class="pl-k">=</span> <span
                class="pl-smi">urls</span>.<span class="pl-en">map</span>(<span class="pl-k">async</span> <span
                class="pl-smi">url</span> <span class="pl-k">=&gt;</span> {
    <span class="pl-k">const</span> <span class="pl-c1">response</span> <span class="pl-k">=</span> <span class="pl-k">await</span> <span
                class="pl-en">fetch</span>(url);
    <span class="pl-k">return</span> <span class="pl-smi">response</span>.<span class="pl-c1">text</span>();
  });

  <span class="pl-c"><span class="pl-c">//</span> 按次序输出</span>
  <span class="pl-k">for</span> (<span class="pl-k">const</span> <span class="pl-c1">textPromise</span> <span
                class="pl-k">of</span> textPromises) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-k">await</span> textPromise);
  }
}</pre>
    </div>
    <p>上面代码中，虽然<code>map</code>方法的参数是<code>async</code>函数，但它是并发执行的，因为只有<code>async</code>函数内部是继发执行，外部不受影响。后面的<code>for..of</code>循环内部使用了<code>await</code>，因此实现了按顺序输出。
    </p>
    <h2><a href="#异步遍历器"  class="anchor" id="user-content-异步遍历器">

    </a>异步遍历器
    </h2>
    <p>《遍历器》一章说过，Iterator 接口是一种数据遍历的协议，只要调用遍历器对象的<code>next</code>方法，就会得到一个对象，表示当前遍历指针所在的那个位置的信息。<code>next</code>方法返回的对象的结构是<code>{value,
        done}</code>，其中<code>value</code>表示当前的数据的值，<code>done</code>是一个布尔值，表示遍历是否结束。</p>
    <p>这里隐含着一个规定，<code>next</code>方法必须是同步的，只要调用就必须立刻返回值。也就是说，一旦执行<code>next</code>方法，就必须同步地得到<code>value</code>和<code>done</code>这两个属性。如果遍历指针正好指向同步操作，当然没有问题，但对于异步操作，就不太合适了。目前的解决方法是，Generator
        函数里面的异步操作，返回一个 Thunk 函数或者 Promise 对象，即<code>value</code>属性是一个 Thunk 函数或者 Promise
        对象，等待以后返回真正的值，而<code>done</code>属性则还是同步产生的。</p>
    <p>目前，有一个<a
            href="https://github.com/tc39/proposal-async-iteration">提案</a>，为异步操作提供原生的遍历器接口，即<code>value</code>和<code>done</code>这两个属性都是异步产生，这称为”异步遍历器“（Async
        Iterator）。</p>
    <h3><a href="#异步遍历的接口"  class="anchor" id="user-content-异步遍历的接口">

    </a>异步遍历的接口
    </h3>
    <p>异步遍历器的最大的语法特点，就是调用遍历器的<code>next</code>方法，返回的是一个 Promise 对象。</p>
    <div class="highlight highlight-source-js"><pre>asyncIterator
  .<span class="pl-c1">next</span>()
  .<span class="pl-en">then</span>(
    ({ value, done }) <span class="pl-k">=&gt;</span> <span class="pl-c"><span class="pl-c">/*</span> ... <span
                class="pl-c">*/</span></span>
  );</pre>
    </div>
    <p>上面代码中，<code>asyncIterator</code>是一个异步遍历器，调用<code>next</code>方法以后，返回一个 Promise 对象。因此，可以使用<code>then</code>方法指定，这个
        Promise 对象的状态变为<code>resolve</code>以后的回调函数。回调函数的参数，则是一个具有<code>value</code>和<code>done</code>两个属性的对象，这个跟同步遍历器是一样的。
    </p>
    <p>我们知道，一个对象的同步遍历器的接口，部署在<code>Symbol.iterator</code>属性上面。同样地，对象的异步遍历器接口，部署在<code>Symbol.asyncIterator</code>属性上面。不管是什么样的对象，只要它的<code>Symbol.asyncIterator</code>属性有值，就表示应该对它进行异步遍历。
    </p>
    <p>下面是一个异步遍历器的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">asyncIterable</span> <span class="pl-k">=</span> <span
            class="pl-en">createAsyncIterable</span>([<span class="pl-s"><span class="pl-pds">'</span>a<span
            class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>b<span
            class="pl-pds">'</span></span>]);
<span class="pl-k">const</span> <span class="pl-c1">asyncIterator</span> <span class="pl-k">=</span> asyncIterable[<span
                class="pl-c1">Symbol</span>.<span class="pl-smi">asyncIterator</span>]();

asyncIterator
.<span class="pl-c1">next</span>()
.<span class="pl-en">then</span>(<span class="pl-smi">iterResult1</span> <span class="pl-k">=&gt;</span> {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(iterResult1); <span class="pl-c"><span class="pl-c">//</span> { value: 'a', done: false }</span>
  <span class="pl-k">return</span> <span class="pl-smi">asyncIterator</span>.<span class="pl-c1">next</span>();
})
.<span class="pl-en">then</span>(<span class="pl-smi">iterResult2</span> <span class="pl-k">=&gt;</span> {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(iterResult2); <span class="pl-c"><span class="pl-c">//</span> { value: 'b', done: false }</span>
  <span class="pl-k">return</span> <span class="pl-smi">asyncIterator</span>.<span class="pl-c1">next</span>();
})
.<span class="pl-en">then</span>(<span class="pl-smi">iterResult3</span> <span class="pl-k">=&gt;</span> {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(iterResult3); <span class="pl-c"><span class="pl-c">//</span> { value: undefined, done: true }</span>
});</pre>
    </div>
    <p>上面代码中，异步遍历器其实返回了两次值。第一次调用的时候，返回一个 Promise 对象；等到 Promise 对象<code>resolve</code>了，再返回一个表示当前数据成员信息的对象。这就是说，异步遍历器与同步遍历器最终行为是一致的，只是会先返回
        Promise 对象，作为中介。</p>
    <p>由于异步遍历器的<code>next</code>方法，返回的是一个 Promise 对象。因此，可以把它放在<code>await</code>命令后面。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">const</span> <span class="pl-c1">asyncIterable</span> <span class="pl-k">=</span> <span
                class="pl-en">createAsyncIterable</span>([<span class="pl-s"><span class="pl-pds">'</span>a<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>b<span
                class="pl-pds">'</span></span>]);
  <span class="pl-k">const</span> <span class="pl-c1">asyncIterator</span> <span
                class="pl-k">=</span> asyncIterable[<span class="pl-c1">Symbol</span>.<span
                class="pl-smi">asyncIterator</span>]();
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-k">await</span> <span
                class="pl-smi">asyncIterator</span>.<span class="pl-c1">next</span>());
  <span class="pl-c"><span class="pl-c">//</span> { value: 'a', done: false }</span>
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-k">await</span> <span
                class="pl-smi">asyncIterator</span>.<span class="pl-c1">next</span>());
  <span class="pl-c"><span class="pl-c">//</span> { value: 'b', done: false }</span>
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-k">await</span> <span
                class="pl-smi">asyncIterator</span>.<span class="pl-c1">next</span>());
  <span class="pl-c"><span class="pl-c">//</span> { value: undefined, done: true }</span>
}</pre>
    </div>
    <p>上面代码中，<code>next</code>方法用<code>await</code>处理以后，就不必使用<code>then</code>方法了。整个流程已经很接近同步处理了。</p>
    <p>注意，异步遍历器的<code>next</code>方法是可以连续调用的，不必等到上一步产生的 Promise 对象<code>resolve</code>以后再调用。这种情况下，<code>next</code>方法会累积起来，自动按照每一步的顺序运行下去。下面是一个例子，把所有的<code>next</code>方法放在<code>Promise.all</code>方法里面。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">asyncGenObj</span> <span class="pl-k">=</span> <span class="pl-en">createAsyncIterable</span>([<span
            class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span>, <span class="pl-s"><span
            class="pl-pds">'</span>b<span class="pl-pds">'</span></span>]);
<span class="pl-k">const</span> [{value<span class="pl-k">:</span> <span class="pl-c1">v1</span>}, {value<span
                class="pl-k">:</span> <span class="pl-c1">v2</span>}] <span class="pl-k">=</span> <span class="pl-k">await</span> <span
                class="pl-c1">Promise</span>.<span class="pl-c1">all</span>([
  <span class="pl-smi">asyncGenObj</span>.<span class="pl-c1">next</span>(), <span
                class="pl-smi">asyncGenObj</span>.<span class="pl-c1">next</span>()
]);

<span class="pl-en">console</span>.<span class="pl-c1">log</span>(v1, v2); <span class="pl-c"><span
                class="pl-c">//</span> a b</span></pre>
    </div>
    <p>另一种用法是一次性调用所有的<code>next</code>方法，然后<code>await</code>最后一步操作。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">writer</span> <span class="pl-k">=</span> <span class="pl-en">openFile</span>(<span
            class="pl-s"><span class="pl-pds">'</span>someFile.txt<span class="pl-pds">'</span></span>);
<span class="pl-smi">writer</span>.<span class="pl-c1">next</span>(<span class="pl-s"><span class="pl-pds">'</span>hello<span
                class="pl-pds">'</span></span>);
<span class="pl-smi">writer</span>.<span class="pl-c1">next</span>(<span class="pl-s"><span class="pl-pds">'</span>world<span
                class="pl-pds">'</span></span>);
<span class="pl-k">await</span> <span class="pl-smi">writer</span>.<span class="pl-en">return</span>();</pre>
    </div>
    <h3><a href="#for-awaitof"  class="anchor" id="user-content-for-awaitof">

    </a>for await...of
    </h3>
    <p>前面介绍过，<code>for...of</code>循环用于遍历同步的 Iterator 接口。新引入的<code>for await...of</code>循环，则是用于遍历异步的 Iterator 接口。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">for</span> <span class="pl-k">await</span> (<span class="pl-k">const</span> <span
                class="pl-c1">x</span> <span class="pl-k">of</span> <span
                class="pl-en">createAsyncIterable</span>([<span class="pl-s"><span class="pl-pds">'</span>a<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>b<span
                class="pl-pds">'</span></span>])) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(x);
  }
}
<span class="pl-c"><span class="pl-c">//</span> a</span>
<span class="pl-c"><span class="pl-c">//</span> b</span></pre>
    </div>
    <p>上面代码中，<code>createAsyncIterable()</code>返回一个异步遍历器，<code>for...of</code>循环自动调用这个遍历器的<code>next</code>方法，会得到一个
        Promise 对象。<code>await</code>用来处理这个 Promise
        对象，一旦<code>resolve</code>，就把得到的值（<code>x</code>）传入<code>for...of</code>的循环体。</p>
    <p><code>for await...of</code>循环的一个用途，是部署了 asyncIterable 操作的异步接口，可以直接放入这个循环。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> body <span class="pl-k">=</span> <span
            class="pl-s"><span class="pl-pds">'</span><span class="pl-pds">'</span></span>;

<span class="pl-k">async</span> <span class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">for</span> <span class="pl-k">await</span>(<span class="pl-k">const</span> <span
                class="pl-c1">data</span> <span class="pl-k">of</span> req) body <span class="pl-k">+=</span> data;
  <span class="pl-k">const</span> <span class="pl-c1">parsed</span> <span class="pl-k">=</span> <span
                class="pl-c1">JSON</span>.<span class="pl-c1">parse</span>(body);
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>got<span class="pl-pds">'</span></span>, parsed);
}</pre>
    </div>
    <p>上面代码中，<code>req</code>是一个 asyncIterable 对象，用来异步读取数据。可以看到，使用<code>for await...of</code>循环以后，代码会非常简洁。</p>
    <p>如果<code>next</code>方法返回的 Promise 对象被<code>reject</code>，<code>for
        await...of</code>就会报错，要用<code>try...catch</code>捕捉。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span class="pl-k">function</span> () {
  <span class="pl-k">try</span> {
    <span class="pl-k">for</span> <span class="pl-k">await</span> (<span class="pl-k">const</span> <span
                class="pl-c1">x</span> <span class="pl-k">of</span> <span class="pl-en">createRejectingIterable</span>()) {
      <span class="pl-en">console</span>.<span class="pl-c1">log</span>(x);
    }
  } <span class="pl-k">catch</span> (e) {
    <span class="pl-en">console</span>.<span class="pl-c1">error</span>(e);
  }
}</pre>
    </div>
    <p>注意，<code>for await...of</code>循环也可以用于同步遍历器。</p>
    <div class="highlight highlight-source-js"><pre>(<span class="pl-k">async</span> <span class="pl-k">function</span> () {
  <span class="pl-k">for</span> <span class="pl-en">await</span> (<span class="pl-k">const</span> <span
                class="pl-c1">x</span> <span class="pl-k">of</span> [<span class="pl-s"><span
                class="pl-pds">'</span>a<span class="pl-pds">'</span></span>, <span class="pl-s"><span
                class="pl-pds">'</span>b<span class="pl-pds">'</span></span>]) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(x);
  }
})();
<span class="pl-c"><span class="pl-c">//</span> a</span>
<span class="pl-c"><span class="pl-c">//</span> b</span></pre>
    </div>
    <h3><a href="#异步-generator-函数"  class="anchor" id="user-content-异步-generator-函数">

    </a>异步 Generator 函数
    </h3>
    <p>就像 Generator 函数返回一个同步遍历器对象一样，异步 Generator 函数的作用，是返回一个异步遍历器对象。</p>
    <p>在语法上，异步 Generator 函数就是<code>async</code>函数与 Generator 函数的结合。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span><span class="pl-k">*</span> <span class="pl-en">gen</span>() {
  <span class="pl-k">yield</span> <span class="pl-s"><span class="pl-pds">'</span>hello<span
                class="pl-pds">'</span></span>;
}
<span class="pl-k">const</span> <span class="pl-c1">genObj</span> <span class="pl-k">=</span> <span
                class="pl-en">gen</span>();
<span class="pl-smi">genObj</span>.<span class="pl-c1">next</span>().<span class="pl-en">then</span>(<span
                class="pl-smi">x</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(x));
<span class="pl-c"><span class="pl-c">//</span> { value: 'hello', done: false }</span></pre>
    </div>
    <p>上面代码中，<code>gen</code>是一个异步 Generator 函数，执行后返回一个异步 Iterator 对象。对该对象调用<code>next</code>方法，返回一个 Promise 对象。</p>
    <p>异步遍历器的设计目的之一，就是 Generator 函数处理同步操作和异步操作时，能够使用同一套接口。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> 同步 Generator 函数</span>
<span class="pl-k">function</span><span class="pl-k">*</span> <span class="pl-en">map</span>(<span class="pl-smi">iterable</span>, <span
                class="pl-smi">func</span>) {
  <span class="pl-k">const</span> <span class="pl-c1">iter</span> <span class="pl-k">=</span> iterable[<span
                class="pl-c1">Symbol</span>.<span class="pl-smi">iterator</span>]();
  <span class="pl-k">while</span> (<span class="pl-c1">true</span>) {
    <span class="pl-k">const</span> {<span class="pl-c1">value</span>, <span class="pl-c1">done</span>} <span
                class="pl-k">=</span> <span class="pl-smi">iter</span>.<span class="pl-c1">next</span>();
    <span class="pl-k">if</span> (done) <span class="pl-k">break</span>;
    <span class="pl-k">yield</span> <span class="pl-en">func</span>(value);
  }
}

<span class="pl-c"><span class="pl-c">//</span> 异步 Generator 函数</span>
<span class="pl-k">async</span> <span class="pl-k">function</span><span class="pl-k">*</span> <span
                class="pl-en">map</span>(<span class="pl-smi">iterable</span>, <span class="pl-smi">func</span>) {
  <span class="pl-k">const</span> <span class="pl-c1">iter</span> <span class="pl-k">=</span> iterable[<span
                class="pl-c1">Symbol</span>.<span class="pl-smi">asyncIterator</span>]();
  <span class="pl-k">while</span> (<span class="pl-c1">true</span>) {
    <span class="pl-k">const</span> {<span class="pl-c1">value</span>, <span class="pl-c1">done</span>} <span
                class="pl-k">=</span> <span class="pl-k">await</span> <span class="pl-smi">iter</span>.<span
                class="pl-c1">next</span>();
    <span class="pl-k">if</span> (done) <span class="pl-k">break</span>;
    <span class="pl-k">yield</span> <span class="pl-en">func</span>(value);
  }
}</pre>
    </div>
    <p>上面代码中，可以看到有了异步遍历器以后，同步 Generator 函数和异步 Generator 函数的写法基本上是一致的。</p>
    <p>下面是另一个异步 Generator 函数的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span><span class="pl-k">*</span> <span class="pl-en">readLines</span>(<span
            class="pl-smi">path</span>) {
  <span class="pl-k">let</span> file <span class="pl-k">=</span> <span class="pl-k">await</span> <span class="pl-en">fileOpen</span>(path);

  <span class="pl-k">try</span> {
    <span class="pl-k">while</span> (<span class="pl-k">!</span><span class="pl-smi">file</span>.<span
                class="pl-c1">EOF</span>) {
      <span class="pl-k">yield</span> <span class="pl-k">await</span> <span class="pl-smi">file</span>.<span
                class="pl-en">readLine</span>();
    }
  } <span class="pl-k">finally</span> {
    <span class="pl-k">await</span> <span class="pl-smi">file</span>.<span class="pl-c1">close</span>();
  }
}</pre>
    </div>
    <p>上面代码中，异步操作前面使用<code>await</code>关键字标明，即<code>await</code>后面的操作，应该返回 Promise
        对象。凡是使用<code>yield</code>关键字的地方，就是<code>next</code>方法的停下来的地方，它后面的表达式的值（即<code>await file.readLine()</code>的值），会作为<code>next()</code>返回对象的<code>value</code>属性，这一点是与同步
        Generator 函数一致的。</p>
    <p>异步 Generator
        函数内部，能够同时使用<code>await</code>和<code>yield</code>命令。可以这样理解，<code>await</code>命令用于将外部操作产生的值输入函数内部，<code>yield</code>命令用于将函数内部的值输出。
    </p>
    <p>上面代码定义的异步 Generator 函数的用法如下。</p>
    <div class="highlight highlight-source-js"><pre>(<span class="pl-k">async</span> <span class="pl-k">function</span> () {
  <span class="pl-k">for</span> <span class="pl-k">await</span> (<span class="pl-k">const</span> <span class="pl-c1">line</span> <span
                class="pl-k">of</span> <span class="pl-en">readLines</span>(filePath)) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(line);
  }
})()</pre>
    </div>
    <p>异步 Generator 函数可以与<code>for await...of</code>循环结合起来使用。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span><span class="pl-k">*</span> <span class="pl-en">prefixLines</span>(<span
            class="pl-smi">asyncIterable</span>) {
  <span class="pl-k">for</span> <span class="pl-en">await</span> (<span class="pl-k">const</span> <span class="pl-c1">line</span> <span
                class="pl-k">of</span> <span class="pl-smi">asyncIterable</span>) {
    <span class="pl-k">yield</span> <span class="pl-s"><span class="pl-pds">'</span>&gt; <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> line;
  }
}</pre>
    </div>
    <p>异步 Generator 函数的返回值是一个异步 Iterator，即每次调用它的<code>next</code>方法，会返回一个 Promise 对象，也就是说，跟在<code>yield</code>命令后面的，应该是一个
        Promise 对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span><span class="pl-k">*</span> <span class="pl-en">asyncGenerator</span>() {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>Start<span
                class="pl-pds">'</span></span>);
  <span class="pl-k">const</span> <span class="pl-c1">result</span> <span class="pl-k">=</span> <span
                class="pl-k">await</span> <span class="pl-en">doSomethingAsync</span>(); <span class="pl-c"><span
                class="pl-c">//</span> (A)</span>
  <span class="pl-k">yield</span> <span class="pl-s"><span class="pl-pds">'</span>Result: <span class="pl-pds">'</span></span><span
                class="pl-k">+</span> result; <span class="pl-c"><span class="pl-c">//</span> (B)</span>
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>Done<span
                class="pl-pds">'</span></span>);
}

<span class="pl-k">const</span> <span class="pl-c1">ag</span> <span class="pl-k">=</span> <span class="pl-en">asyncGenerator</span>();
<span class="pl-smi">ag</span>.<span class="pl-c1">next</span>().<span class="pl-en">then</span>({value, done} <span
                class="pl-k">=&gt;</span> {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
})</pre>
    </div>
    <p>上面代码中，<code>ag</code>是<code>asyncGenerator</code>函数返回的异步 Iterator 对象。调用<code>ag.next()</code>以后，<code>asyncGenerator</code>函数内部的执行顺序如下。
    </p>
    <ol>
        <li>打印出<code>Start</code>。</li>
        <li><code>await</code>命令返回一个 Promise 对象，但是程序不会停在这里，继续往下执行。</li>
        <li>程序在<code>B</code>处暂停执行，<code>yield</code>命令立刻返回一个 Promise 对象，该对象就是<code>ag.next()</code>的返回值。</li>
        <li><code>A</code>处<code>await</code>命令后面的那个 Promise 对象 resolved，产生的值放入<code>result</code>变量。</li>
        <li><code>B</code>处的 Promise 对象
            resolved，<code>then</code>方法指定的回调函数开始执行，该函数的参数是一个对象，<code>value</code>的值是表达式<code>'Result： ' + result</code>的值，<code>done</code>属性的值是<code>false</code>。
        </li>
    </ol>
    <p>A 和 B 两行的作用类似于下面的代码。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">return</span> <span class="pl-k">new</span> <span
            class="pl-en">Promise</span>((<span class="pl-smi">resolve</span>, <span class="pl-smi">reject</span>) <span
            class="pl-k">=&gt;</span> {
  <span class="pl-en">doSomethingAsync</span>()
  .<span class="pl-en">then</span>(<span class="pl-smi">result</span> <span class="pl-k">=&gt;</span> {
     <span class="pl-en">resolve</span>({
       value<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>Result: <span
                class="pl-pds">'</span></span><span class="pl-k">+</span>result,
       done<span class="pl-k">:</span> <span class="pl-c1">false</span>,
     });
  });
});</pre>
    </div>
    <p>如果异步 Generator 函数抛出错误，会被 Promise 对象<code>reject</code>，然后抛出的错误被<code>catch</code>方法捕获。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span><span class="pl-k">*</span> <span class="pl-en">asyncGenerator</span>() {
  <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span
                class="pl-s"><span class="pl-pds">'</span>Problem!<span class="pl-pds">'</span></span>);
}

<span class="pl-en">asyncGenerator</span>()
.<span class="pl-c1">next</span>()
.<span class="pl-en">catch</span>(<span class="pl-smi">err</span> <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(err)); <span class="pl-c"><span
                class="pl-c">//</span> Error: Problem!</span></pre>
    </div>
    <p>注意，普通的 async 函数返回的是一个 Promise 对象，而异步 Generator 函数返回的是一个异步 Iterator 对象。可以这样理解，async 函数和异步 Generator
        函数，是封装异步操作的两种方法，都用来达到同一种目的。区别在于，前者自带执行器，后者通过<code>for await...of</code>执行，或者自己编写执行器。下面就是一个异步 Generator 函数的执行器。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">takeAsync</span>(<span class="pl-smi">asyncIterable</span>, <span
            class="pl-smi">count</span> <span class="pl-k">=</span> <span class="pl-c1">Infinity</span>) {
  <span class="pl-k">const</span> <span class="pl-c1">result</span> <span class="pl-k">=</span> [];
  <span class="pl-k">const</span> <span class="pl-c1">iterator</span> <span class="pl-k">=</span> asyncIterable[<span
                class="pl-c1">Symbol</span>.<span class="pl-smi">asyncIterator</span>]();
  <span class="pl-k">while</span> (<span class="pl-smi">result</span>.<span class="pl-c1">length</span> <span
                class="pl-k">&lt;</span> count) {
    <span class="pl-k">const</span> {<span class="pl-c1">value</span>, <span class="pl-c1">done</span>} <span
                class="pl-k">=</span> <span class="pl-k">await</span> <span class="pl-smi">iterator</span>.<span
                class="pl-c1">next</span>();
    <span class="pl-k">if</span> (done) <span class="pl-k">break</span>;
    <span class="pl-smi">result</span>.<span class="pl-c1">push</span>(value);
  }
  <span class="pl-k">return</span> result;
}</pre>
    </div>
    <p>上面代码中，异步 Generator 函数产生的异步遍历器，会通过<code>while</code>循环自动执行，每当<code>await
        iterator.next()</code>完成，就会进入下一轮循环。一旦<code>done</code>属性变为<code>true</code>，就会跳出循环，异步遍历器执行结束。</p>
    <p>下面是这个自动执行器的一个使用实例。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">f</span>() {
  <span class="pl-k">async</span> <span class="pl-k">function</span><span class="pl-k">*</span> <span
                class="pl-en">gen</span>() {
    <span class="pl-k">yield</span> <span class="pl-s"><span class="pl-pds">'</span>a<span
                class="pl-pds">'</span></span>;
    <span class="pl-k">yield</span> <span class="pl-s"><span class="pl-pds">'</span>b<span
                class="pl-pds">'</span></span>;
    <span class="pl-k">yield</span> <span class="pl-s"><span class="pl-pds">'</span>c<span
                class="pl-pds">'</span></span>;
  }

  <span class="pl-k">return</span> <span class="pl-k">await</span> <span class="pl-en">takeAsync</span>(<span
                class="pl-en">gen</span>());
}

<span class="pl-en">f</span>().<span class="pl-en">then</span>(<span class="pl-k">function</span> (<span class="pl-smi">result</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(result); <span class="pl-c"><span
                class="pl-c">//</span> ['a', 'b', 'c']</span>
})</pre>
    </div>
    <p>异步 Generator 函数出现以后，JavaScript 就有了四种函数形式：普通函数、async 函数、Generator 函数和异步 Generator
        函数。请注意区分每种函数的不同之处。基本上，如果是一系列按照顺序执行的异步操作（比如读取文件，然后写入新内容，再存入硬盘），可以使用 async
        函数；如果是一系列产生相同数据结构的异步操作（比如一行一行读取文件），可以使用异步 Generator 函数。</p>
    <p>异步 Generator 函数也可以通过<code>next</code>方法的参数，接收外部传入的数据。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">writer</span> <span class="pl-k">=</span> <span class="pl-en">openFile</span>(<span
            class="pl-s"><span class="pl-pds">'</span>someFile.txt<span class="pl-pds">'</span></span>);
<span class="pl-smi">writer</span>.<span class="pl-c1">next</span>(<span class="pl-s"><span class="pl-pds">'</span>hello<span
                class="pl-pds">'</span></span>); <span class="pl-c"><span class="pl-c">//</span> 立即执行</span>
<span class="pl-smi">writer</span>.<span class="pl-c1">next</span>(<span class="pl-s"><span class="pl-pds">'</span>world<span
                class="pl-pds">'</span></span>); <span class="pl-c"><span class="pl-c">//</span> 立即执行</span>
<span class="pl-k">await</span> <span class="pl-smi">writer</span>.<span class="pl-en">return</span>(); <span
                class="pl-c"><span class="pl-c">//</span> 等待写入结束</span></pre>
    </div>
    <p>上面代码中，<code>openFile</code>是一个异步 Generator 函数。<code>next</code>方法的参数，向该函数内部的操作传入数据。每次<code>next</code>方法都是同步执行的，最后的<code>await</code>命令用于等待整个写入操作结束。
    </p>
    <p>最后，同步的数据结构，也可以使用异步 Generator 函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span><span class="pl-k">*</span> <span class="pl-en">createAsyncIterable</span>(<span
            class="pl-smi">syncIterable</span>) {
  <span class="pl-k">for</span> (<span class="pl-k">const</span> <span class="pl-c1">elem</span> <span
                class="pl-k">of</span> syncIterable) {
    <span class="pl-k">yield</span> elem;
  }
}</pre>
    </div>
    <p>上面代码中，由于没有异步操作，所以也就没有使用<code>await</code>关键字。</p>
    <h3><a href="#yield-语句"  class="anchor" id="user-content-yield-语句">

    </a>yield* 语句
    </h3>
    <p><code>yield*</code>语句也可以跟一个异步遍历器。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span><span class="pl-k">*</span> <span class="pl-en">gen1</span>() {
  <span class="pl-k">yield</span> <span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span>;
  <span class="pl-k">yield</span> <span class="pl-s"><span class="pl-pds">'</span>b<span class="pl-pds">'</span></span>;
  <span class="pl-k">return</span> <span class="pl-c1">2</span>;
}

<span class="pl-k">async</span> <span class="pl-k">function</span><span class="pl-k">*</span> <span
                class="pl-en">gen2</span>() {
  <span class="pl-c"><span class="pl-c">//</span> result 最终会等于 2</span>
  <span class="pl-k">const</span> <span class="pl-c1">result</span> <span class="pl-k">=</span> <span
                class="pl-k">yield</span><span class="pl-k">*</span> <span class="pl-en">gen1</span>();
}</pre>
    </div>
    <p>上面代码中，<code>gen2</code>函数里面的<code>result</code>变量，最后的值是<code>2</code>。</p>
    <p>与同步 Generator 函数一样，<code>for await...of</code>循环会展开<code>yield*</code>。</p>
    <div class="highlight highlight-source-js"><pre>(<span class="pl-k">async</span> <span class="pl-k">function</span> () {
  <span class="pl-k">for</span> <span class="pl-k">await</span> (<span class="pl-k">const</span> <span
                class="pl-c1">x</span> <span class="pl-k">of</span> <span class="pl-en">gen2</span>()) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(x);
  }
})();
<span class="pl-c"><span class="pl-c">//</span> a</span>
<span class="pl-c"><span class="pl-c">//</span> b</span></pre>
    </div>
</div>
</body>
</html>
